package com.qingsong.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.qingsong.dto.RoleInsertDTO;
import com.qingsong.dto.RolePageDTO;
import com.qingsong.dto.RoleUpdateDTO;
import com.qingsong.entity.Menu;
import com.qingsong.entity.Role;
import com.qingsong.entity.RoleMenu;
import com.qingsong.exception.ServiceException;
import com.qingsong.mapper.RoleMapper;
import com.qingsong.mapper.RoleMenuMapper;
import com.qingsong.mapper.UserMapper;
import com.qingsong.mapper.UserRoleMapper;
import com.qingsong.service.RoleService;
import com.qingsong.vo.MenuSimpleListVO;
import com.qingsong.vo.RoleSimpleListVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mybatisflex.core.paginate.Page;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.qingsong.entity.table.MenuTableDef.MENU;
import static com.qingsong.entity.table.RoleMenuTableDef.ROLE_MENU;
import static com.qingsong.entity.table.RoleTableDef.ROLE;
import static com.qingsong.entity.table.UserRoleTableDef.USER_ROLE;

/**
 * 角色表 服务层实现。
 *
 * @author qingsong
 * @since 2024-12-22
 */
@Service
@CacheConfig(cacheNames = "role")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role>  implements RoleService{

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Override
    @CacheEvict(allEntries = true)
    public boolean insert(RoleInsertDTO dto) {
        Role role = BeanUtil.copyProperties(dto, Role.class);
        
        // 标题不能重复
        if (QueryChain.of(mapper)
                .where(ROLE.TITLE.eq(role.getTitle()))
                .exists()) {
            throw new ServiceException("角色标题已存在");
        }
        role.setCreated(LocalDateTime.now());
        role.setUpdated(LocalDateTime.now());
        return mapper.insert(role) > 0;
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public Role select(Long id) {
        return QueryChain.of(mapper)
                .where(ROLE.ID.eq(id))
                .withRelations()
                .one();
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean update(RoleUpdateDTO dto) {

        // 标题查重
        if (QueryChain.of(mapper)
                .where(ROLE.TITLE.eq(dto.getTitle()))
                .and(ROLE.ID.ne(dto.getId()))
                .exists()) {
            throw new ServiceException("角色标题已存在");
        }

        // 组装 entity 实体类
        Role role = BeanUtil.copyProperties(dto, Role.class);

        // 设置 entity 默认值
        role.setUpdated(LocalDateTime.now());
        return UpdateChain.of(role).where(ROLE.ID.eq(role.getId())).update();
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public boolean delete(Long id) {
        // 删除中间表记录
        UpdateChain.of(roleMenuMapper)
                .where(ROLE_MENU.FK_ROLE_ID.eq(id))
                .remove();
        UpdateChain.of(userRoleMapper)
                .where(USER_ROLE.FK_ROLE_ID.eq(id))
                .remove();
        // 删除角色
        return mapper.deleteById(id) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public boolean deleteBatch(List<Long> ids) {
        // 删除中间表记录
        UpdateChain.of(roleMenuMapper)
                .where(ROLE_MENU.FK_ROLE_ID.in(ids))
                .remove();
        UpdateChain.of(userRoleMapper)
                .where(USER_ROLE.FK_ROLE_ID.in(ids))
                .remove();
        // 删除角色
        return mapper.deleteBatchByIds(ids) == ids.size();
    }

    @Cacheable(key = "#root.methodName",
            unless = "#result == null")
    @Override
    public List<RoleSimpleListVO> simpleList() {
        // 查询所有菜单
        List<Role> roles = QueryChain.of(mapper)
                .orderBy(ROLE.IDX.asc(), ROLE.ID.desc())
                .withRelations()
                .list();

        // 组装 VO 实体类
        List<RoleSimpleListVO> roleSimpleListVOS = new ArrayList<>();
        roles.forEach(role -> {
            RoleSimpleListVO roleSimpleListVO = BeanUtil.copyProperties(role, RoleSimpleListVO.class);
            if (role.getTitle() != null) {
                roleSimpleListVO.setTitle(role.getTitle());
            }
            roleSimpleListVOS.add(roleSimpleListVO);
        });
        return roleSimpleListVOS;
    }

    @Override
    @Cacheable(key = "#root.methodName+':'+#p0.toString()", unless = "#result == null")
    public Page<Role> page(RolePageDTO dto) {
        QueryChain<Role> queryChain = QueryChain.of(mapper);
        queryChain.orderBy(ROLE.IDX.asc(), ROLE.ID.desc());

        // title条件
        String title = dto.getTitle();
        if (ObjectUtil.isNotNull(title)) {
            queryChain.where(ROLE.TITLE.like(title));
        }

        return queryChain.withRelations()
                .page(new Page<>(dto.getPageNum(), dto.getPageSize()));
    }

    @Cacheable(key = "#root.methodName + ':' + #p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public List<Long> listMenuIds(Long id) {
        return QueryChain.of(RoleMenu.class)
                .select(ROLE_MENU.FK_MENU_ID)
                .where(ROLE_MENU.FK_ROLE_ID.eq(id))
                .objListAs(Long.class);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateMenus(RoleUpdateDTO dto) {
        Long roleId = dto.getId();
        List<Long> menuIds = dto.getMenuIds();

        // 在中间表中删除该角色的全部菜单记录
        UpdateChain.of(roleMenuMapper)
                .where(ROLE_MENU.FK_ROLE_ID.eq(roleId))
                .remove();

        // 新菜单列表为空，直接返回即可
        if (menuIds.isEmpty()) {
            return true;
        }

        // 在中间表中批量添加该角色的新菜单记录
        List<RoleMenu> roleMenus = new ArrayList<>();
        for (Long menuId : menuIds) {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setFkRoleId(roleId);
            roleMenu.setFkMenuId(menuId);
            roleMenu.setCreated(LocalDateTime.now());
            roleMenu.setUpdated(LocalDateTime.now());
            roleMenus.add(roleMenu);
        }

        return roleMenuMapper.insertBatch(roleMenus) == menuIds.size();
    }

}
